const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const stylus = require('stylus');
const mysql = require('mysql');
const cors=require("cors");
const session = require('express-session');
const MySQLStore = require('express-mysql-session')(session);

const fs = require('fs');
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' });

// cookie 的最长失效时间，1小时
const COOKIE_MAXAGE = 60 * 60 * 1000;
const SESSION_SECRET = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

const Utils = require('./utils/utils');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
// 项目路由
const projectRouter = require('./routes/project')
// 任务类型路由
const tasktypeRouter = require('./routes/tasktype')
// 任务路由
const taskRouter = require('./routes/task')
// 报告
const reportRouter = require('./routes/report')

const dbOption = require('./db/db').dbOption;
const sessionConnection = mysql.createConnection(dbOption);
const sessionStore = new MySQLStore({
  // 是否自动校验并清除过期session
  clearExpired: true,
  // 过期session清除频率，90秒
  checkExpirationInterval: 90 * 1000,
  // 最长有效时间，默认1小时
  expiration: 60 * 60 * 1000,
  // 如果没有session表，是否创建
  createDatabaseTable: true,
  // 创建连接池时的连接数
  connectionLimit: 1,
  // 是否在存储关闭时终止数据库连接。
  // 该选项的默认值取决于是否将连接传递给构造函数。
  // 如果一个连接对象被传递给构造函数，这个选项的默认值是false。
  endConnectionOnClose: true,
  charset: 'utf8mb4_bin',
  schema: {
    tableName: 'session',
    columnNames: {
      session_id: 'session_id',
      expires: 'expires',
      data: 'data'
    }
  }
}, sessionConnection);

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev', { stream: accessLogStream }));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser('dailycookie', {
  // domain: null,
  // encode: () => {},
  // 指定时间格式
  // expires: '',
  // 浏览器不允许脚本操作 document.cookie 去更改 cookie。设置为true可以避免被 xss 攻击拿到 cookie
  httpOnly: true,
  // 指定 cookie 过期时间
  maxAge: COOKIE_MAXAGE,
  // 指定 cookie 影响到的路径
  path: '/',
  // sameSite: true,
  // 当 secure 值为 true 时，在 HTTPS 中才有效；反之，cookie 在 HTTP 中是有效
  secure: false,
  signed: true
}));

app.use(session({
  // 设置cookie中，保存session的字段名称，默认为connect.sid
  // name: 'dailysession',
  // 配置加密字符串，会在原有加密基础上和这个字符串拼起来去加密
  // 目的是：增加安全性，防止客户端恶意伪造
  // key: 'adlisyseisno',
  // 通过设置的secret字符串，来计算hash值并放在cookie中，使产生的signedCookie防篡改
  // 应该是给定后就不变的值，否则每次请求都会导致session更新而失效
  secret: SESSION_SECRET,
  // session的存储方式，默认为存放在内存中，我们可以自定义redis等
  store: sessionStore,
  // 生成一个新的session_id时，默认为使用uid2这个npm包
  // genid: '',
  // 每个请求都重新设置一个cookie，默认为false
  rolling: true,
  // 即使session没有被修改，也保存session值，默认为true
  resave: false,
  // 强制未初始化的session保存到数据库
  saveUninitialized: false,
  // 设置存放sessionid的cookie的相关选项
  cookie: ('dailycookie', (new Date()).toLocaleString(), {
    name: 'dailycookie',
    httpOnly: true,
    path: '/',
    maxAge: COOKIE_MAXAGE,
    secure: false,
    // resave: false
  })
}));
app.use(stylus.middleware({
  src: path.join(__dirname, 'public'),
  compress: true
}));
app.use(express.static(path.join(__dirname, 'public')));

app.use(cors());

app.all('*', (req, res, next) => {
  // // 设置允许跨域的域名，*代表允许任意域名跨域
  // res.header('Access-Control-Allow-Origin', 'http://localhost:3001');
  // // 允许的header类型
  // res.header('Access-Control-Allow-Headers', 'content-type');
  // // 跨域允许的请求方式
  // res.header('Access-Control-Allow-Methods', 'DELETE,PUT,POST,GET,OPTIONS');

  if (req.url == '/login' || req.url == '/register' || req.url == '/logout') {
    next()
  } else {
    if (!req.session || !req.session.isLogin) {
      res.redirect('/logout')
    } else {
      // 更新cookie失效时间
      res.cookie('isLogin', true, {
          expires: new Date(Date.now() + COOKIE_MAXAGE),
          maxAge: COOKIE_MAXAGE,
          signed: true
      })
      next()
    }
  }
})
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/project', projectRouter)
app.use('/tasktype', tasktypeRouter)
app.use('/task', taskRouter)
app.use('/report', reportRouter)

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

// 使用 stylus watch 监听目录未找对方法
// 使用 stylus middleware 监听未起作用
// 使用 node 监听 stylus 文件变化
let exec = require('child_process').exec;
function execute(cmd) {
  exec(cmd, (error, stdout, stderr) => {
    if (error) {
      console.error(error)
    } else {
      console.log((new Date()).toLocaleString() + ' ' + stdout.replace('compiled', '').trim() + ' 重新编译成功！')
    }
  })
}

// 遍历 views 目录，编译 styl 文件
Utils.toCss(path.join(__dirname, 'views'), (filename) => {
  if (filename.indexOf('.styl') != -1) {
    // 检查文件是否存
    fs.access(filename.replace('.styl', '.css'), fs.constants.F_OK, (err) => {
      // 如果未编译，则编译
      if (err) {
        execute('stylus ' + path.join(__dirname, 'views', filename) + ' -c -o ' + path.join(__dirname, 'views', filename).replace('.styl', '.css'));
      }
    });
  }
})

console.log(`正在监听.styl文件变化...`)
let watchSt
fs.watch(path.join(__dirname, 'views'), {
  recursive: true
}, (event, filename) => {
  // styl文件变化，重新编译
  if (filename && event == 'change' && filename.indexOf('.styl') != -1) {
    if (watchSt) {
      clearTimeout(watchSt);
    }
    watchSt = setTimeout(() => {
      execute('stylus ' + path.join(__dirname, 'views', filename) + ' -c -o ' + path.join(__dirname, 'views', filename).replace('.styl', '.css'));
    }, 100);
  }
})

module.exports = app;
